/*
 **	Query.cpp
 **
 **	Published / author: 2006-03-29 / grymse@alhem.net
 **/
/*
Copyright (C) 2006 Anders Hedstrom

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifdef _WIN32
#pragma warning(disable:4786)
#endif

#ifdef _WIN32
#include <afxdb.h>
#include <sql.h>
#endif

#include "Query.h"
#include "Database.h"
#include "IError.h"
#include "Session.h"


#ifdef ODBCW_NAMESPACE
namespace ODBCW_NAMESPACE {
#endif


Query::Query(Database& dbin)
:m_db(dbin)
,m_odb(m_db.grabdb())
,m_hstmt(NULL)
,m_hres(NULL)
,m_currentcolumn(0)
,m_results(0)
,m_fetchrows(0)
,m_qcols(0)
,m_currentcol(0)
{
}


Query::Query(Database& dbin,const std::string& sql)
:m_db(dbin)
,m_odb(m_db.grabdb())
,m_hstmt(NULL)
,m_hres(NULL)
,m_currentcolumn(0)
,m_results(0)
,m_fetchrows(0)
,m_qcols(0)
,m_currentcol(0)
{
	execute(sql);
}


Query::~Query()
{
	if (m_hstmt)
	{
		SQLFreeHandle(SQL_HANDLE_STMT, m_hstmt);
	}
	if (m_hres)
	{
		delete m_hres;
	}
	if (m_odb)
	{
		m_db.freedb(m_odb);
	}
}


short Query::execute(const std::string& sql)	// query, no result
{
	SQLRETURN retcode;
	m_last_query = sql;
	if (m_odb && m_hres)
	{
		error("Query::execute(): Query busy");
	}
	if (m_odb && !m_hres)
	{
		m_results = 0;
		m_fetchrows = 0;
		m_qcols = 0;
		m_currentcol = 0;

		// Free statement handle
		if (m_hstmt)
		{
			SQLFreeHandle(SQL_HANDLE_STMT, m_hstmt);
		}

		// Allocate statement handle (STMT)
		retcode = SQLAllocHandle(SQL_HANDLE_STMT, static_cast<SQLHDBC>(m_odb -> GetHandle()), &m_hstmt);
		if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO)
		{
			error("Query::execute(): Allocate statement handle (STMT) failed");
			return 0;	 // 0 failed
		}

		retcode = SQLExecDirect(m_hstmt, (unsigned char *)sql.c_str(), sql.size());
		if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
		{
			Session *tmp = dynamic_cast<Session *>(m_odb);
			if (tmp)
			{
				tmp -> ShowErrorInfo(retcode, SQL_HANDLE_STMT, m_hstmt);
			}
		}
		if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO)
		{
			error("Query::execute(): SQL Query failed:");
			error(sql);
			return 0;	 // 0 failed
		}
		m_results = num_rows();
		return 1;
	}
	return 0;
}


bool Query::get_result(const std::string& sql,char **attrs)
{
	UNUSED_ALWAYS(attrs);
	if (m_odb && m_hres)
	{
		error("Query::get_result(): Query busy");
	}
	if (m_odb && !m_hres)
	{
		if (execute(sql))
		{
			return odbc_store_result();
		}
		else
		{
			error("Query::get_result(): Query failed");
		}
	}
	return false;
}


void Query::free_result()
{
	if (m_odb && m_hres)
	{
		delete m_hres;
		m_hres = NULL;
	}
	while (m_nmap.size())
	{
		std::map<std::string,int>::iterator it = m_nmap.begin();
		m_nmap.erase(it);
	}
}


bool Query::get_result(SQLSMALLINT func)
{
	if (m_odb && m_hres)
	{
		error("Query::get_result(): Query busy");
	}
	if (m_odb && !m_hres)
	{
		SQLRETURN retcode = SQL_ERROR;
		bool result = false;

		m_results = 0;
		m_fetchrows = 0;
		m_qcols = 0;
		m_currentcol = 0;

		// Free statement handle
		if (m_hstmt)
		{
			SQLFreeHandle(SQL_HANDLE_STMT, m_hstmt);
		}

		// Allocate statement handle (STMT)
		retcode = SQLAllocHandle(SQL_HANDLE_STMT, static_cast<SQLHDBC>(m_odb -> GetHandle()), &m_hstmt);
		if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO)
		{
			error("Query::execute(): Allocate statement handle (STMT) failed");
			return 0;	 // 0 failed
		}

		switch (func)
		{
		case SQLGET_CATALOGS:
			retcode = SQLTables(m_hstmt, // catalogs, schemas, tables, table types
				(unsigned char *)SQL_ALL_CATALOGS, SQL_NTS,
				(unsigned char *)NULL, 0,
				(unsigned char *)NULL, 0,
				(unsigned char *)NULL, 0
				);
			break;
		case SQLGET_SCHEMAS:
			retcode = SQLTables(m_hstmt, // catalogs, schemas, tables, table types
				(unsigned char *)NULL, 0,
				(unsigned char *)SQL_ALL_SCHEMAS, SQL_NTS,
				(unsigned char *)NULL, 0,
				(unsigned char *)NULL, 0
				);
			break;
		case SQLGET_TABLES:
			retcode = SQLTables(m_hstmt, // catalogs, schemas, tables, table types
				(unsigned char *)SQL_ALL_CATALOGS, SQL_NTS,
				(unsigned char *)NULL, 0,
				(unsigned char *)"%%", SQL_NTS, // TODO: single %?
				(unsigned char *)"TABLE", 5
				);
			break;
		case SQLGET_VIEWS:
			retcode = SQLTables(m_hstmt, // catalogs, schemas, tables, table types
				(unsigned char *)SQL_ALL_CATALOGS, SQL_NTS,
				(unsigned char *)NULL, 0,
				(unsigned char *)"%%", SQL_NTS, // TODO: single %?
				(unsigned char *)"VIEW", 4
				);
			break;
		case SQLGET_TABLETYPES:
			retcode = SQLTables(m_hstmt, // catalogs, schemas, tables, table types
				(unsigned char *)"", 0,
				(unsigned char *)"", 0,
				(unsigned char *)"", 0,
				(unsigned char *)SQL_ALL_TABLE_TYPES, SQL_NTS
				);
			break;
		}

		if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
		{
			Session *tmp = dynamic_cast<Session *>(m_odb);
			if (tmp)
			{
				tmp -> ShowErrorInfo(retcode, SQL_HANDLE_STMT, m_hstmt);
			}
		}
		if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
		{
			result = true;
		}
		else
		{
		}

		if (result)
		{
			return odbc_store_result();
		}
		else
		{
			error("Query::get_result(): Query failed");
		}
	}
	return false;
}


bool Query::get_result(SQLSMALLINT func, const std::string& catalog, const std::string& schema, const std::string& tablename)
{
	if (m_odb && m_hres)
	{
		error("Query::get_result(): Query busy");
	}
	if (m_odb && !m_hres)
	{
		SQLRETURN retcode = SQL_ERROR;
		bool result = false;

		m_results = 0;
		m_fetchrows = 0;
		m_qcols = 0;
		m_currentcol = 0;

		// Free statement handle
		if (m_hstmt)
		{
			SQLFreeHandle(SQL_HANDLE_STMT, m_hstmt);
		}

		// Allocate statement handle (STMT)
		retcode = SQLAllocHandle(SQL_HANDLE_STMT, static_cast<SQLHDBC>(m_odb -> GetHandle()), &m_hstmt);
		if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO)
		{
			error("Query::execute(): Allocate statement handle (STMT) failed");
			return 0;	 // 0 failed
		}

		switch (func)
		{
		case SQLGET_COLUMNS:
			retcode = SQLColumns(m_hstmt,
				(unsigned char *) (catalog.size() ? catalog.c_str() : NULL), (SQLSMALLINT) (catalog.size() ? SQL_NTS : 0),
				(unsigned char *) (schema.size() ? schema.c_str() : NULL), (SQLSMALLINT) (schema.size() ? SQL_NTS : 0),
				(unsigned char *)tablename.c_str(), (SQLSMALLINT)SQL_NTS,
				(unsigned char *)NULL, (SQLSMALLINT)0);
			break;
		case SQLGET_SPECIALCOLUMNS:
			retcode = SQLSpecialColumns(m_hstmt,
				SQL_BEST_ROWID,					 // SQL_ROWVER,
				(unsigned char *) (catalog.size() ? catalog.c_str() : NULL), (SQLSMALLINT) (catalog.size() ? SQL_NTS : 0),
				(unsigned char *) (schema.size() ? schema.c_str() : NULL), (SQLSMALLINT) (schema.size() ? SQL_NTS : 0),
				(unsigned char *)tablename.c_str(), (SQLSMALLINT)SQL_NTS,
				SQL_SCOPE_TRANSACTION, // SQL_SCOPE_SESSION,
				SQL_NULLABLE);
			break;
		case SQLGET_PRIMARYKEYS:
			retcode = SQLPrimaryKeys(m_hstmt,
				(unsigned char *) (catalog.size() ? catalog.c_str() : NULL), (SQLSMALLINT) (catalog.size() ? SQL_NTS : 0),
				(unsigned char *) (schema.size() ? schema.c_str() : NULL), (SQLSMALLINT) (schema.size() ? SQL_NTS : 0),
				(unsigned char *)tablename.c_str(), (SQLSMALLINT)SQL_NTS
				);
			break;
		}

		if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO)
		{
			Session *tmp = dynamic_cast<Session *>(m_odb);
			if (tmp)
			{
				tmp -> ShowErrorInfo(retcode, SQL_HANDLE_STMT, m_hstmt);
			}
		}
		if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
		{
			result = true;
		}
		else
		{
		}

		if (result)
		{
			return odbc_store_result();
		}
		else
		{
			error("Query::get_result(): Query failed");
		}
	}
	return false;
}


bool Query::fetch_row()
{
	m_currentcolumn = 0; // ODBC columns start at 1
	m_fetchrows++;
	if (m_odb && m_hstmt && m_hres)
	{
		SQLRETURN retcode = SQLFetch(m_hstmt);
		if (retcode == SQL_SUCCESS)
		{
			return true;
		}
	}
	return false;
}


long Query::insert_id()
{
	return -1;
}


long Query::num_rows()
{
	SQLINTEGER rowCount;

	if (m_hstmt)
	{
		SQLRETURN retcode = SQLRowCount(m_hstmt, &rowCount);
		if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
		{
		}
	}
	else
	{
		rowCount = -1;
	}
	return rowCount;
}


bool Query::is_null(int x)
{
	if (m_odb && m_hstmt && m_hres && x >= 0 && x < m_hres -> m_num_cols)
	{
		SQLHCOL *col = m_hres -> m_cols[x + 1];
		if (col -> cbBuffer == SQL_NULL_DATA)
			return true;
	}
	return false;
}


bool Query::is_null()
{
	return is_null(m_currentcolumn++);
}


bool Query::is_null(const std::string& x)
{
	int index = m_nmap[x] - 1;
	if (index >= 0)
		return is_null(index);
	error("Column name lookup failure: " + x);
	return false;
}


const char *Query::getstr(int x)
{
	SQLHCOL *col;
	char slask[80];		 // sprintf format

	if (m_odb && m_hstmt && m_hres && x >= 0 && x < m_hres -> m_num_cols)
	{
		col = m_hres -> m_cols[x + 1];
		if (col -> nCType == SQL_C_CHAR && !strcmp(col -> szTypeName,"SQL_DECIMAL"))
		{
			double d = atof(col -> buffer -> String);
			char *s;
			if (!col -> tmpstr)
			{
				col -> tmpstr = new char[80];
			}
			sprintf(slask,"%%%d.%df",col -> nColumnSizePtr,col -> nDecimalDigitsPtr);
			sprintf(col -> tmpstr, slask, d);
			if ((s = strstr(col -> tmpstr,".")) != NULL)
			{
				*s = m_db.GetDecimalChar();
			}
			return col -> tmpstr;
		}
		else
		switch (col -> nCType)
		{
		case SQL_C_CHAR:
			return col -> buffer -> String;
		case SQL_C_WCHAR:			 // unicode
			return col -> buffer -> String;
//		case SQL_C_TCHAR:
//			return col -> buffer -> String;
		case SQL_C_FLOAT:
			if (!col -> tmpstr)
			{
				col -> tmpstr = new char[80];
			}
			sprintf(slask,"%%%d.%df",col -> nColumnSizePtr,col -> nDecimalDigitsPtr);
			sprintf(col -> tmpstr, slask, col -> buffer -> f);
			return col -> tmpstr;
		case SQL_C_DOUBLE:
			if (!col -> tmpstr)
			{
				col -> tmpstr = new char[80];
			}
			sprintf(slask,"%%%d.%df",col -> nColumnSizePtr,col -> nDecimalDigitsPtr);
			sprintf(col -> tmpstr, slask, col -> buffer -> d);
			return col -> tmpstr;
		case SQL_C_BIT:
			if (!col -> tmpstr)
			{
				col -> tmpstr = new char[80];
			}
			sprintf(col -> tmpstr,"%u",col -> buffer -> c & 1);
			return col -> tmpstr;
		case SQL_C_STINYINT:
			if (!col -> tmpstr)
			{
				col -> tmpstr = new char[20];
			}
			sprintf(slask,"%%%dd",col -> nColumnSizePtr);
			sprintf(col -> tmpstr, slask, col -> buffer -> c);
			return col -> tmpstr;
		case SQL_C_SSHORT:
			if (!col -> tmpstr)
			{
				col -> tmpstr = new char[20];
			}
			sprintf(slask,"%%%dd",col -> nColumnSizePtr);
			sprintf(col -> tmpstr, slask, col -> buffer -> s);
			return col -> tmpstr;
		case SQL_C_SLONG:
			if (!col -> tmpstr)
			{
				col -> tmpstr = new char[20];
			}
			strcpy(slask,"%ld");
			sprintf(col -> tmpstr, slask, col -> buffer -> l);
			return col -> tmpstr;
		case SQL_C_SBIGINT: // _int64
			if (!col -> tmpstr)
			{
				col -> tmpstr = new char[80];
			}
			sprintf(slask,"%%%dI64d",col -> nColumnSizePtr);
			sprintf(col -> tmpstr, slask, col -> buffer -> bigint);
			return col -> tmpstr;
		case SQL_C_BINARY:
			return col -> buffer -> String;
		case SQL_C_TYPE_DATE:		 // SQL_DATE_STRUCT
			if (!col -> tmpstr)
			{
				col -> tmpstr = new char[20];
			}
			sprintf(col -> tmpstr,"%d-%02d-%02d",
				col -> buffer -> date.year,
				col -> buffer -> date.month,
				col -> buffer -> date.day);
			return col -> tmpstr;
		case SQL_C_TYPE_TIME:
			if (!col -> tmpstr)
			{
				col -> tmpstr = new char[20];
			}
			sprintf(col -> tmpstr,"%02d:%02d:%02d",
				col -> buffer -> time.hour,
				col -> buffer -> time.minute,
				col -> buffer -> time.second);
			return col -> tmpstr;
		case SQL_C_TYPE_TIMESTAMP:
			if (!col -> tmpstr)
			{
				col -> tmpstr = new char[80];
			}
			sprintf(col -> tmpstr,"%d-%02d-%02d %02d:%02d:%02d", //.%u",
				col -> buffer -> timestamp.year,
				col -> buffer -> timestamp.month,
				col -> buffer -> timestamp.day,
				col -> buffer -> timestamp.hour,
				col -> buffer -> timestamp.minute,
				col -> buffer -> timestamp.second);
			return col -> tmpstr;

		case SQL_INTERVAL_MONTH:
		case SQL_INTERVAL_YEAR:
		case SQL_INTERVAL_YEAR_TO_MONTH:
		case SQL_INTERVAL_DAY:
		case SQL_INTERVAL_HOUR:
		case SQL_INTERVAL_MINUTE:
		case SQL_INTERVAL_SECOND:
		case SQL_INTERVAL_DAY_TO_HOUR:
		case SQL_INTERVAL_DAY_TO_MINUTE:
		case SQL_INTERVAL_DAY_TO_SECOND:
		case SQL_INTERVAL_HOUR_TO_MINUTE:
		case SQL_INTERVAL_HOUR_TO_SECOND:
		case SQL_INTERVAL_MINUTE_TO_SECOND:
			break;

		default:
			{
				char msg[200];
				sprintf(msg, "Query::getstr():	Conversion not supported (%s)\n",col -> szTypeName);
				error(msg);
			}
			break;
		}
	}
	return NULL;
}


double Query::getnum(int x)
{
	SQLHCOL *col;

	if (m_odb && m_hstmt && m_hres && x >= 0 && x < m_hres -> m_num_cols)
	{
		col = m_hres -> m_cols[x + 1];
		switch (col -> nCType)
		{
		case SQL_C_FLOAT:
			return col -> buffer -> f;
		case SQL_C_DOUBLE:
			return col -> buffer -> d;
		case SQL_C_CHAR:
			if (!strcmp(col -> szTypeName,"SQL_DECIMAL"))
			{
				double d = atof(col -> buffer -> String);
				return d;
			}
			{
				char msg[200];
				sprintf(msg, "Query::getnum():	Conversion not supported (%s)\n",col -> szTypeName);
				error(msg);
			}
			break;

		default:
			{
				char msg[200];
				sprintf(msg, "Query::getnum():	Conversion not supported (%s)\n",col -> szTypeName);
				error(msg);
			}
			break;
		}
	}
	return 0;
}


long Query::getval(int x)
{
	SQLHCOL *col;
	long l;

	if (m_odb && m_hstmt && m_hres && x >= 0 && x < m_hres -> m_num_cols)
	{
		col = m_hres -> m_cols[x + 1];
		switch (col -> nCType)
		{
		case SQL_C_BIT:
			return col -> buffer -> c & 1;
		case SQL_C_STINYINT:
			return col -> buffer -> c;
		case SQL_C_SSHORT:
			return col -> buffer -> s;
		case SQL_C_SLONG:
			return col -> buffer -> l;
		case SQL_C_SBIGINT:
			l = (long)col -> buffer -> bigint;
			return static_cast<long>(col -> buffer -> bigint);

		default:
			{
				char msg[200];
				sprintf(msg, "Query::getval():	Conversion not supported (%s)\n",col -> szTypeName);
				error(msg);
			}
			break;
		}
	}
	return 0L;
}


_int64 Query::getbigint(int x)
{
	SQLHCOL *col;

	if (m_odb && m_hstmt && m_hres && x >= 0 && x < m_hres -> m_num_cols)
	{
		col = m_hres -> m_cols[x + 1];
		switch (col -> nCType)
		{
		case SQL_C_SBIGINT:
			return col -> buffer -> bigint;

		default:
			{
				char msg[200];
				sprintf(msg, "Query::getbigint():	Conversion not supported (%s)\n",col -> szTypeName);
				error(msg);
			}
			break;
		}
	}
	return 0L;
}


SQL_INTERVAL_STRUCT *Query::getinterval(int x)
{
	SQLHCOL *col;

	if (m_odb && m_hstmt && m_hres && x >= 0 && x < m_hres -> m_num_cols)
	{
		col = m_hres -> m_cols[x + 1];
		switch (col -> nCType)
		{
		case SQL_INTERVAL_MONTH:
		case SQL_INTERVAL_YEAR:
		case SQL_INTERVAL_YEAR_TO_MONTH:
		case SQL_INTERVAL_DAY:
		case SQL_INTERVAL_HOUR:
		case SQL_INTERVAL_MINUTE:
		case SQL_INTERVAL_SECOND:
		case SQL_INTERVAL_DAY_TO_HOUR:
		case SQL_INTERVAL_DAY_TO_MINUTE:
		case SQL_INTERVAL_DAY_TO_SECOND:
		case SQL_INTERVAL_HOUR_TO_MINUTE:
		case SQL_INTERVAL_HOUR_TO_SECOND:
		case SQL_INTERVAL_MINUTE_TO_SECOND:
			return &col -> buffer -> interval;

		default:
			{
				char msg[200];
				sprintf(msg, "Query::getinterval():	Conversion not supported (%s)\n",col -> szTypeName);
				error(msg);
			}
			break;
		}
	}
	return NULL;
}


const char *Query::getstr()
{
	return getstr(m_currentcolumn++);
}


double Query::getnum()
{
	return getnum(m_currentcolumn++);
}


long Query::getval()
{
	return getval(m_currentcolumn++);
}


_int64 Query::getbigint()
{
	return getbigint(m_currentcolumn++);
}


SQL_INTERVAL_STRUCT *Query::getinterval()
{
	return getinterval(m_currentcolumn++);
}


const char *Query::getstr(const std::string& x)
{
	int index = m_nmap[x] - 1;
	if (index >= 0)
		return getstr(index);
	error("Column name lookup failure: " + x);
	return "";
}


double Query::getnum(const std::string& x)
{
	int index = m_nmap[x] - 1;
	if (index >= 0)
		return getnum(index);
	error("Column name lookup failure: " + x);
	return 0;
}


long Query::getval(const std::string& x)
{
	int index = m_nmap[x] - 1;
	if (index >= 0)
		return getval(index);
	error("Column name lookup failure: " + x);
	return 0;
}


_int64 Query::getbigint(const std::string& x)
{
	int index = m_nmap[x] - 1;
	if (index >= 0)
		return getbigint(index);
	error("Column name lookup failure: " + x);
	return 0;
}


SQL_INTERVAL_STRUCT *Query::getinterval(const std::string& x)
{
	int index = m_nmap[x] - 1;
	if (index >= 0)
		return getinterval(index);
	error("Column name lookup failure: " + x);
	return 0;
}


const char *Query::get_string(const std::string& sql)
{
	m_tmpstr = "";
	if (get_result(sql))
	{
		if (fetch_row())
		{
			m_tmpstr = getstr();
		}
		free_result();
	}
	return m_tmpstr.c_str();
}


double Query::get_num(const std::string& sql)
{
	double l = 0;

	if (get_result(sql))
	{
		if (fetch_row())
		{
			l = getnum();
		}
		free_result();
	}
	return l;
}


long Query::get_count(const std::string& sql)
{
	long l = 0;

	if (get_result(sql))
	{
		if (fetch_row())
		{
			l = getval();
		}
		free_result();
	}
	return l;
}


bool Query::odbc_store_result()
{
	SQLRETURN retcode;
	SQLHCOL *tmp;
	SQLSMALLINT q;
	long buf_len;

	retcode = SQLNumResultCols(m_hstmt, &q);
	m_qcols = q;

	m_hres = new DSQLHRES(q);

	for (int i = 1; i <= m_hres -> m_num_cols; i++)
	{
		tmp = new SQLHCOL;
		retcode = SQLDescribeCol(m_hstmt, (SQLSMALLINT)i, tmp -> szColumnName, STR_LEN,
					&tmp -> nNameLengthPtr,&tmp -> nDataTypePtr,&tmp -> nColumnSizePtr,
					&tmp -> nDecimalDigitsPtr,&tmp -> nNullablePtr);
		m_nmap[ (char *)tmp -> szColumnName] = i;
		if (tmp -> nColumnSizePtr == 0x7fffffff)
		{
			buf_len = BUF_LEN;
		}
		else
		{
			buf_len = (tmp -> nColumnSizePtr > BUF_LEN ? tmp -> nColumnSizePtr : BUF_LEN) + 1;
		}
		tmp -> buffer = (SQLUNIONBUFFER *)new char[buf_len];
		*tmp -> szTypeName = 0;
		switch (tmp -> nDataTypePtr)
		{
		case SQL_CHAR:
			strcpy(tmp -> szTypeName, "SQL_CHAR");
			tmp -> nCType = SQL_C_TCHAR; // SQL_C_CHAR;
			break;
		case SQL_VARCHAR:
			strcpy(tmp -> szTypeName, "SQL_VARCHAR");
			tmp -> nCType = SQL_C_TCHAR; // SQL_C_CHAR;
			break;
		case SQL_LONGVARCHAR:
			strcpy(tmp -> szTypeName, "SQL_LONGVARCHAR");
			tmp -> nCType = SQL_C_TCHAR; // SQL_C_CHAR;
			break;
		case SQL_WCHAR:
			strcpy(tmp -> szTypeName, "SQL_WCHAR");
			tmp -> nCType = SQL_C_TCHAR; // SQL_C_WCHAR;
			break;
		case SQL_WVARCHAR:
			strcpy(tmp -> szTypeName, "SQL_WVARCHAR");
			tmp -> nCType = SQL_C_TCHAR; // SQL_C_WCHAR;
			break;
		case SQL_WLONGVARCHAR:
			strcpy(tmp -> szTypeName, "SQL_WLONGVARCHAR");
			tmp -> nCType = SQL_C_TCHAR; // SQL_C_WCHAR;
			break;
		case SQL_DECIMAL:
			strcpy(tmp -> szTypeName, "SQL_DECIMAL");
			tmp -> nCType = SQL_C_CHAR;
			break;
		case SQL_NUMERIC:
			strcpy(tmp -> szTypeName, "SQL_NUMERIC");
			tmp -> nCType = SQL_C_CHAR;
			break;
		case SQL_SMALLINT:
			strcpy(tmp -> szTypeName, "SQL_SMALLINT");
			tmp -> nCType = SQL_C_SSHORT;	 // SQL_C_USHORT
			break;
		case SQL_INTEGER:
			strcpy(tmp -> szTypeName, "SQL_INTEGER");
			tmp -> nCType = SQL_C_SLONG;		// SQL_C_ULONG
			break;
		case SQL_REAL:
			strcpy(tmp -> szTypeName, "SQL_REAL");
			tmp -> nCType = SQL_C_FLOAT;
			break;
		case SQL_FLOAT:
			strcpy(tmp -> szTypeName, "SQL_FLOAT");
			tmp -> nCType = SQL_C_DOUBLE;
			break;
		case SQL_DOUBLE:
			strcpy(tmp -> szTypeName, "SQL_DOUBLE");
			tmp -> nCType = SQL_C_DOUBLE;
			break;
		case SQL_BIT:
			strcpy(tmp -> szTypeName, "SQL_BIT");
			tmp -> nCType = SQL_C_BIT;
			break;
		case SQL_TINYINT:
			strcpy(tmp -> szTypeName, "SQL_TINYINT");
			tmp -> nCType = SQL_C_STINYINT; // SQL_C_UTINYINT
			break;
		case SQL_BIGINT:
			strcpy(tmp -> szTypeName, "SQL_BIGINT");
			tmp -> nCType = SQL_C_SBIGINT;		// SQL_C_UBININT
			break;
		case SQL_BINARY:
			strcpy(tmp -> szTypeName, "SQL_BINARY");
			tmp -> nCType = SQL_C_BINARY;
			break;
		case SQL_VARBINARY:
			strcpy(tmp -> szTypeName, "SQL_VARBINARY");
			tmp -> nCType = SQL_C_BINARY;
			break;
		case SQL_LONGVARBINARY:
			strcpy(tmp -> szTypeName, "SQL_LONGVARBINARY");
			tmp -> nCType = SQL_C_BINARY;
			break;
		case SQL_TYPE_DATE:
			strcpy(tmp -> szTypeName, "SQL_TYPE_DATE");
			tmp -> nCType = SQL_C_TYPE_DATE;
			break;
		case SQL_TYPE_TIME:
			strcpy(tmp -> szTypeName, "SQL_TYPE_TIME");
			tmp -> nCType = SQL_C_TYPE_TIME;
			break;
		case SQL_TYPE_TIMESTAMP:
			strcpy(tmp -> szTypeName, "SQL_TYPE_TIMESTAMP");
			tmp -> nCType = SQL_C_TYPE_TIMESTAMP;
			break;
		case SQL_INTERVAL_MONTH:
			strcpy(tmp -> szTypeName, "SQL_INTERVAL_MONTH");
			tmp -> nCType = SQL_C_INTERVAL_MONTH;
			break;
		case SQL_INTERVAL_YEAR:
			strcpy(tmp -> szTypeName, "SQL_INTERVAL_YEAR");
			tmp -> nCType = SQL_C_INTERVAL_YEAR;
			break;
		case SQL_INTERVAL_YEAR_TO_MONTH:
			strcpy(tmp -> szTypeName, "SQL_INTERVAL_YEAR_TO_MONTH");
			tmp -> nCType = SQL_C_INTERVAL_YEAR_TO_MONTH;
			break;
		case SQL_INTERVAL_DAY:
			strcpy(tmp -> szTypeName, "SQL_INTERVAL_DAY");
			tmp -> nCType = SQL_C_INTERVAL_DAY;
			break;
		case SQL_INTERVAL_HOUR:
			strcpy(tmp -> szTypeName, "SQL_INTERVAL_HOUR");
			tmp -> nCType = SQL_C_INTERVAL_HOUR;
			break;
		case SQL_INTERVAL_MINUTE:
			strcpy(tmp -> szTypeName, "SQL_INTERVAL_MINUTE");
			tmp -> nCType = SQL_C_INTERVAL_MINUTE;
			break;
		case SQL_INTERVAL_SECOND:
			strcpy(tmp -> szTypeName, "SQL_INTERVAL_SECOND");
			tmp -> nCType = SQL_C_INTERVAL_SECOND;
			break;
		case SQL_INTERVAL_DAY_TO_HOUR:
			strcpy(tmp -> szTypeName, "SQL_INTERVAL_DAY_TO_HOUR");
			tmp -> nCType = SQL_C_INTERVAL_DAY_TO_HOUR;
			break;
		case SQL_INTERVAL_DAY_TO_MINUTE:
			strcpy(tmp -> szTypeName, "SQL_INTERVAL_DAY_TO_MINUTE");
			tmp -> nCType = SQL_C_INTERVAL_DAY_TO_MINUTE;
			break;
		case SQL_INTERVAL_DAY_TO_SECOND:
			strcpy(tmp -> szTypeName, "SQL_INTERVAL_DAY_TO_SECOND");
			tmp -> nCType = SQL_C_INTERVAL_DAY_TO_SECOND;
			break;
		case SQL_INTERVAL_HOUR_TO_MINUTE:
			strcpy(tmp -> szTypeName, "SQL_INTERVAL_HOUR_TO_MINUTE");
			tmp -> nCType = SQL_C_INTERVAL_HOUR_TO_MINUTE;
			break;
		case SQL_INTERVAL_HOUR_TO_SECOND:
			strcpy(tmp -> szTypeName, "SQL_INTERVAL_HOUR_TO_SECOND");
			tmp -> nCType = SQL_C_INTERVAL_HOUR_TO_SECOND;
			break;
		case SQL_INTERVAL_MINUTE_TO_SECOND:
			strcpy(tmp -> szTypeName, "SQL_INTERVAL_MINUTE_TO_SECOND");
			tmp -> nCType = SQL_C_INTERVAL_MINUTE_TO_SECOND;
			break;
		case SQL_GUID:
			strcpy(tmp -> szTypeName, "SQL_GUID");
			tmp -> nCType = SQL_C_CHAR;
			break;
		default:
			sprintf(tmp -> szTypeName, "<Unknown type id#%d>", tmp -> nDataTypePtr);
			tmp -> nCType = -1;
			error("Query::get_result(): Unknown result set column data type");
		}
		memset(tmp -> buffer -> String, 0, buf_len);
		m_hres -> add_col(tmp);
		tmp -> cbBuffer = buf_len;
		tmp -> tmpstr = NULL;
		retcode = SQLBindCol(m_hstmt, (SQLSMALLINT)i, tmp -> nCType, (SQLPOINTER)tmp -> buffer, buf_len, &tmp -> cbBuffer);
	}
	return true;
}


bool Query::GetColumnInfo(short x,SQLHCOL& hc)
{
	if (m_odb && m_hstmt && m_hres && x >= 0 && x < m_hres -> m_num_cols)
	{
		memmove(&hc, m_hres -> m_cols[x + 1], sizeof(SQLHCOL));
		hc.next = NULL;
		hc.buffer = NULL;
		hc.tmpstr = NULL;
		return true;
	}
	return false;
}


bool Query::GotMore()
{
	return (m_fetchrows < m_results) ? true : false;
}


DSQLHRES::DSQLHRES(int q) : m_num_cols((SQLSMALLINT)q)
{
	m_colbase = NULL;
	m_cols = new SQLHCOL *[q + 1];
	for (int i = 0; i <= q; i++)
	{
		m_cols[i] = NULL;
	}
	m_qcols = 0;
}


DSQLHRES::~DSQLHRES()
{
	SQLHCOL *tmp;

	while (m_colbase)
	{
		tmp = m_colbase -> next;
		// ta bort members i m_colbase
		delete m_colbase -> buffer;
		if (m_colbase -> tmpstr)
		{
			delete m_colbase -> tmpstr;
		}
		// ta bort m_colbase
		delete m_colbase;
		m_colbase = tmp;
	}
	delete m_cols;
}


void DSQLHRES::add_col(SQLHCOL *item)
{
	SQLHCOL *q = m_colbase;

	item -> next = NULL;
	if (!q)
	{
		m_colbase = item;
	}
	else
	{
		while (q -> next)
		{
			q = q -> next;
		}
		q -> next = item;
	}
	m_qcols++;
	m_cols[m_qcols] = item;
}


void Query::ReturnRowAsXML(std::string& xml,const std::string& strObjectName,long dwFlags)
{
	SQLHCOL *col;
	std::string typ;
	std::string str;
	bool endtag = (dwFlags & DQ_XML_ENDTAG) ? true : false;
	bool cdata = (dwFlags & DQ_XML_CDATASTRINGS) ? true : false;
	bool typeinfo = (dwFlags & DQ_XML_TYPEINFO) ? true : false;
	bool append = (dwFlags & DQ_XML_APPEND) ? true : false;
	bool xmlbegin = (dwFlags & DQ_XML_BEGIN) ? true : false;
	bool xmlend = (dwFlags & DQ_XML_END) ? true : false;
	char slask[32000];

	if (m_odb && m_hstmt && m_hres)
	{
		if (!append)
		{
			xml = "";
		}
		if (xmlbegin)
		{
			xml += "<xml version=\"1.0\" encoding=\"ISO-8859-1\">\n";
		}
		if (strObjectName != "")
		{
			xml += "<";
			xml += strObjectName;
			xml += ">\n";
		}
		for (int x = 0; x < m_hres -> m_num_cols; x++)
		{
			col = m_hres -> m_cols[x + 1];
			// xml'safe data std::string
			// ---
			if (typeinfo)
			{
				sprintf(slask," TYPE=\"%s\"", col -> szTypeName);
				typ = slask;
			}
			else
			{
				typ = "";
			}
			if (!*getstr(x))
			{
				sprintf(slask,"<%s%s/>", col -> szColumnName, typ);
				str = slask;
			}
			else
			if (cdata && (col -> nCType == SQL_C_CHAR || col -> nCType == SQL_C_TCHAR))
			{
				sprintf(slask,"<%s%s><![CDATA[%s]]></%s>\n", col -> szColumnName, typ, m_db.xmlsafestr(getstr(x)), col -> szColumnName);
				str = slask;
			}
			else
			{
				sprintf(slask,"<%s%s>%s</%s>\n", col -> szColumnName, typ, m_db.xmlsafestr(getstr(x)), col -> szColumnName);
				str = slask;
			}
			xml += str;
		}
		if (endtag && strObjectName != "")
		{
			xml += "</";
			xml += strObjectName;
			xml += ">\n";
		}
		if (xmlend)
		{
			xml += "</xml>\n";
		}
	}
}


int Query::num_cols()
{
	return m_qcols;
}


bool Query::getcol(std::string& colname,std::string& coltype,int& width,int& dec,int& nullable)
{
	return getcol(m_currentcol++, colname, coltype, width, dec, nullable);
}


bool Query::getcol(int x,std::string& colname,std::string& coltype,int& width,int& dec,int& nullable)
{
	if (x < m_qcols)
	{
		SQLHCOL *col;

		col = m_hres -> m_cols[x + 1];
		colname = (char *)(col -> szColumnName);
		coltype = col -> szTypeName;
		width = col -> nColumnSizePtr;
		dec = col -> nDecimalDigitsPtr;
		nullable = col -> nNullablePtr;
		return true;
	}
	else
	{
		return false;
	}
}


void Query::error(const std::string& text)
{
	if (m_db.GetErrHandler())
	{
		m_db.GetErrHandler() -> error(m_db, *this, text);
	}
}


bool Query::Connected()
{
	return true; // TODO: implement
}


Database& Query::GetDatabase() const
{
	return m_db;
}


const std::string& Query::GetLastQuery()
{
	return m_last_query;
}


const std::string& Query::GetError()
{
	m_errtext = "";
	if (m_odb)
	{
		m_odb->GetLastError(m_sql_state, m_errtext);
	}
	return m_errtext;
}


int Query::GetErrno()
{
	if (m_odb)
	{
		return m_odb->GetLastError(m_sql_state, m_errtext);
	}
	return -1;
}


#ifdef ODBCW_NAMESPACE
} // namespace ODBCW_NAMESPACE {
#endif
